package database

import (
	"context"
	"dustbe/util"
	"fmt"
	"strings"
	"github.com/neo4j/neo4j-go-driver/v5/neo4j"
)

// 查询博客
func GetBlogNumber() (int, error) {
	cypher := "match (n:Blog) return count(n)"

	session := Neo4jDriver.NewSession(neo4j.SessionConfig{
		AccessMode: neo4j.AccessModeRead,
	})
	defer session.Close(context.TODO())

	result, err := session.Run(context.TODO(), cypher, nil)
	if err != nil {
		fmt.Println(err.Error())
		return -1, err
	}

	var blogNum int = 0
	for result.Next(context.TODO()) {
		blogNumInter, _ := result.Record().Get("count(n)")
		blogNum = int(blogNumInter.(int64))
	}

	return blogNum, nil
}

// 拿到区间 min_seq 到 max_seq 内的博客
func GetRangeBlogBySeq(min_seq, max_seq int) ([]map[string]interface{}, error) {
	cypher := fmt.Sprintf("match (n:Blog) where n.seq >= %d and n.seq <= %d return n", min_seq, max_seq)

	session := Neo4jDriver.NewSession(neo4j.SessionConfig{
		AccessMode: neo4j.AccessModeRead,
	})
	defer session.Close(context.TODO())

	result, err := session.Run(context.TODO(), cypher, nil)
	if err != nil {
		fmt.Println(err.Error())
		return nil, err
	}

	nodes := make([]map[string]interface{}, max_seq-min_seq+1)

	cur_index := 0
	for result.Next(context.TODO()) {
		res, _ := result.Record().Get("n")
		node := res.(neo4j.Node)
		nodes[cur_index] = node.Props
		cur_index++
	}

	return nodes, err
}

// 根据页数获取博客 pageId 从 1 开始
func GetBlogByPage(pageId, blogNumOnePage int) ([]map[string]interface{}, error) {
	skipPages := (pageId - 1) * blogNumOnePage
	cypher := fmt.Sprintf("match (n:Blog) return n order by n.seq desc skip %d limit %d", skipPages, blogNumOnePage)

	session := Neo4jDriver.NewSession(neo4j.SessionConfig{
		AccessMode: neo4j.AccessModeRead,
	})
	defer session.Close(context.TODO())

	result, err := session.Run(context.TODO(), cypher, nil)
	if err != nil {
		fmt.Println(err.Error())
		return nil, err
	}

	nodes := make([]map[string]interface{}, blogNumOnePage)

	cur_index := 0
	for result.Next(context.TODO()) {
		res, _ := result.Record().Get("n")
		node := res.(neo4j.Node)
		nodes[cur_index] = node.Props
		cur_index++
	}

	return nodes, err
}

// 根据博客名字查找博客
func QueryBlogByName(blogName string) (map[string]interface{}, error) {
	cypher := fmt.Sprintf("match (n:Blog{name:\"%s\"}) return n", blogName)

	session := Neo4jDriver.NewSession(neo4j.SessionConfig{
		AccessMode: neo4j.AccessModeRead,
	})
	defer session.Close(context.TODO())

	result, err := session.Run(context.TODO(), cypher, nil)

	if err != nil {
		fmt.Println(err.Error())
		return nil, err
	}

	var resultNode map[string]interface{}

	for result.Next(context.TODO()) {
		res, _ := result.Record().Get("n")
		node := res.(neo4j.Node)
		resultNode = node.Props
	}

	return resultNode, err
}

// 根据 seq 查找博客
func QueryBlogBySeq(seq int) (map[string]interface{}, error) {
	cypher := fmt.Sprintf("match (n:Blog{seq:%d}) return n", seq)
	session := Neo4jDriver.NewSession(neo4j.SessionConfig{
		AccessMode: neo4j.AccessModeRead,
	})
	defer session.Close(context.TODO())

	result, err := session.Run(context.TODO(), cypher, nil)

	if err != nil {
		fmt.Println(err.Error())
		return nil, err
	}

	var resultNode map[string]interface{}

	for result.Next(context.TODO()) {
		res, _ := result.Record().Get("n")
		node := res.(neo4j.Node)
		resultNode = node.Props
	}

	return resultNode, err
}

// 找到话题 T 下的博客数量
func GetBlogNumByTopicName(tagName string) (int, error) {
	cypher := fmt.Sprintf("match (n:Blog)-[r:belongTo]->(m:Topic{name:\"%s\"}) return count(n)", tagName)

	session := Neo4jDriver.NewSession(neo4j.SessionConfig{
		AccessMode: neo4j.AccessModeRead,
	})
	defer session.Close(context.TODO())

	result, err := session.Run(context.TODO(), cypher, nil)
	if err != nil {
		fmt.Println(err.Error())
		return -1, err
	}

	var blogNum int = 0
	for result.Next(context.TODO()) {
		blogNumInter, _ := result.Record().Get("count(n)")
		blogNum = int(blogNumInter.(int64))
	}

	return blogNum, nil
}

// 根据话题名称找到博客，分页
func GetAllBlogsByTopicName(tagName string, begin int, end int) ([]map[string]interface{}, error) {
	skip := begin
	limit := end - begin + 1
	cypher := fmt.Sprintf("match (n:Blog)-[r:belongTo]->(m:Topic{name:\"%s\"}) return n skip %d limit %d", tagName, skip, limit)

	session := Neo4jDriver.NewSession(neo4j.SessionConfig{
		AccessMode: neo4j.AccessModeRead,
	})
	defer session.Close(context.TODO())

	result, err := session.Run(context.TODO(), cypher, nil)
	if err != nil {
		fmt.Println(err.Error())
		return nil, err
	}

	nodes := make([]map[string]interface{}, 0)

	for result.Next(context.TODO()) {
		res, _ := result.Record().Get("n")
		node := res.(neo4j.Node)
		nodes = append(nodes, node.Props)
	}

	return nodes, err
}

// 根据博客名字推荐博客
func RecommandByBlogName(blogName string, limits int) ([]map[string]interface{}, error) {
	cypher := fmt.Sprintf("match (n:Blog{name:\"%s\"})-[r]-(m:Topic) with m  match (m:Topic)-[r]-(n:Blog) return n limit 30", blogName)

	session := Neo4jDriver.NewSession(neo4j.SessionConfig{
		AccessMode: neo4j.AccessModeRead,
	})
	defer session.Close(context.TODO())

	result, err := session.Run(context.TODO(), cypher, nil)

	if err != nil {
		fmt.Println(err.Error())
		return nil, err
	}

	blogs := make([]map[string]interface{}, 0)

	exists := make(map[int64]bool)

	for result.Next(context.TODO()) {
		res, _ := result.Record().Get("n")
		node := res.(neo4j.Node)
		if node.Props["name"].(string) != blogName {
			_, ok := exists[node.Props["seq"].(int64)]
			if !ok {
				blogs = append(blogs, node.Props)
				exists[node.Props["seq"].(int64)] = true
			}
		}
		if len(blogs) == limits {
			break
		}
	}

	return blogs, err
}

// 创建新的blog，其中tags为逗号隔开的字符串
// blog belong to topic
func CreateBlog(name string, tagsString string, adjustTime string, imgUrl string, seq int) (bool, error) {
	tags := strings.Split(tagsString, ",")
	var cypherTags string = ""
	for index, tag := range tags {
		tag, _ = util.AddDoubleQuote(tag)
		cypherTags += tag
		if index < len(tags)-1 {
			cypherTags += ","
		}
	}
	cypherTags = "[" + cypherTags + "]"
	adjustTime, _ = util.AddDoubleQuote(adjustTime)
	imgUrl, _ = util.AddDoubleQuote(imgUrl)
	createBlogCypher := fmt.Sprintf("merge (n:Blog{name:\"%s\", tags:%s, adjustTime:%s, imgUrl:%s, seq: %d})",
		name, cypherTags, adjustTime, imgUrl, seq)
	belongToCypher := "merge (n)-[r:belongTo]->(m)"

	session := Neo4jDriver.NewSession(neo4j.SessionConfig{
		AccessMode: neo4j.AccessModeWrite,
	})
	defer session.Close(context.TODO())

	for _, tag := range tags {
		mergeTopicCypher := fmt.Sprintf("merge (m:Topic{name:\"%s\"})", tag)
		cypher := createBlogCypher + " " + mergeTopicCypher + " " + belongToCypher
		_, err := session.Run(context.TODO(), cypher, nil)
		if err != nil {
			fmt.Println(err.Error())
			return false, err
		}
	}

	return true, nil
}

// 维护 blog 全局索引值
func GetBlogCurrentIndex() (int, error) {
	cypher := "match (n:Constant{name:\"blogCurIndex\"}) return n.value limit 1"

	session := Neo4jDriver.NewSession(neo4j.SessionConfig{
		AccessMode: neo4j.AccessModeRead,
	})

	defer session.Close(context.TODO())
	result, err := session.Run(context.TODO(), cypher, nil)

	if err != nil {
		fmt.Println(err.Error())
		return -1, err
	}

	var curIndex int = -1
	for result.Next(context.TODO()) {
		index, _ := result.Record().Get("n.value")
		curIndex = int(index.(int64))
	}
	return curIndex, nil
}

func SetBlogCurrentIndex(index int) (bool, error) {
	cypher := fmt.Sprintf("match (n:Constant{name:\"blogCurIndex\"}) set n.value=%d return n", index)
	session := Neo4jDriver.NewSession(neo4j.SessionConfig{
		AccessMode: neo4j.AccessModeWrite,
	})

	defer session.Close(context.TODO())
	_, err := session.Run(context.TODO(), cypher, nil)

	if err != nil {
		fmt.Println(err.Error())
		return false, err
	}
	return true, nil
}

func DeleteBlogBySeq(seq int) (bool, error) {
	cypher := fmt.Sprintf("match (n:Blog{seq:%d}) detach delete n", seq)
	session := Neo4jDriver.NewSession(neo4j.SessionConfig{
		AccessMode: neo4j.AccessModeWrite,
	})
	defer session.Close(context.TODO())
	_, err := session.Run(context.TODO(), cypher, nil)
	if err != nil {
		fmt.Println(err.Error())
		return false, err
	}
	return true, nil
}

func ModifyBlogBySeq(seq int, name, tagsString, adjustTime, imgUrl, text string) (bool, error) {
	tags := strings.Split(tagsString, ",")
	var cypherTags string = ""
	for index, tag := range tags {
		tag, _ = util.AddDoubleQuote(tag)
		cypherTags += tag
		if index < len(tags)-1 {
			cypherTags += ","
		}
	}
	cypherTags = "[" + cypherTags + "]"
	adjustTime, _ = util.AddDoubleQuote(adjustTime)
	imgUrl, _ = util.AddDoubleQuote(imgUrl)

	modifyBlogCypher := fmt.Sprintf("match (n:Blog{seq:%d}) set n.name=\"%s\" set n.tags=%s set n.adjustTime=%s set n.imgUrl=%s return n",
		seq, name, cypherTags, adjustTime, imgUrl)

	session := Neo4jDriver.NewSession(neo4j.SessionConfig{
		AccessMode: neo4j.AccessModeWrite,
	})
	defer session.Close(context.TODO())
	_, err := session.Run(context.TODO(), modifyBlogCypher, nil)
	if err != nil {
		fmt.Println(err.Error())
		return false, err
	}

	return true, nil
}

// nameString是前端输入的参数的空格split后用逗号隔开
func SearchBlogsByName(namesString string) ([]map[string]interface{}, error) {
	names := strings.Split(namesString, ",")
	cypher := "match (n:Blog) where n.name="
	reg := util.GetNeo4jRegSearchString(names)
	cypher += reg + " return n"

	session := Neo4jDriver.NewSession(neo4j.SessionConfig{
		AccessMode: neo4j.AccessModeRead,
	})
	defer session.Close(context.TODO())

	result, err := session.Run(context.TODO(), cypher, nil)
	if err != nil {
		fmt.Println(err.Error())
		return nil, err
	}

	nodes := make([]map[string]interface{}, 0)

	for result.Next(context.TODO()) {
		res, _ := result.Record().Get("n")
		node := res.(neo4j.Node)
		nodes = append(nodes, node.Props)
	}

	return nodes, nil
}
